﻿using System;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;

namespace com.uplus.common
{
    public class MemoryCacheHelper<T>
    {
        public static IMemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions());
        static Dictionary<string, object> LOCKS = new Dictionary<string, object>();

        #region  Contains
        /// <summary>
        /// Check from MemoryCache.Items
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Contains(object key)
        {
            return MemoryCache.Get(key) != null;
        }
        #endregion

        #region Get
        /// <summary>
        /// Get from MemoryCache.Items
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T Get<T>(object key)
        {
            try
            {
                return (T)MemoryCache.Get<T>(key);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region  Set by instance
        /// <summary>
        /// Set MemoryCache.Items
        /// </summary>
        /// <param name="key"></param>
        /// <param name="instance"></param>
        public static void Set(object key, object instance)
        {
            MemoryCache.Set(key, instance);
        }
        #endregion

        #region  Remove
        /// <summary>
        /// Remove from MemoryCache.Items
        /// </summary>
        /// <param name="key"></param>
        public static void Remove(object key)
        {
            MemoryCache.Remove(key);
        }
        #endregion

        #region  SetCache by MemoryCacheOptions<T>
        /// <summary>
        /// Set MemoryCache.Cache
        /// </summary>
        /// <param name="key"></param>
        /// <param name="instance"></param>
        public static void SetCache(string key, MemoryCacheOptions<T> options)
        {
            options.CacheIt(key);
        }
        #endregion

        #region RemoveCache
        /// <summary>
        /// Rmove item from MemoryCache.Cache
        /// </summary>
        /// <param name="cachedKey">Cached Key</param>
        public static void RemoveCache(string cachedKey)
        {
            MemoryCache.Remove(cachedKey);
        }
        #endregion

        #region  ContainsCache
        /// <summary>
        /// 从Cache中检查是否存在缓存对象
        /// </summary>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public static bool ContainsCache(string cacheKey)
        {
            return MemoryCache.Get(cacheKey) != null;
        }
        #endregion

        #region  GetFromStatic
        /// <summary>
        /// Get or insert from MemoryCache.Cache
        /// </summary>
        /// <typeparam name="TParam">param type to data load function</typeparam>
        /// <typeparam name="TValue">cached data type</typeparam>
        /// <param name="cachedKey">cache key</param>
        /// <param name="param">param to data load function</param>
        /// <param name="loadRequired">When data load reqruied</param>
        /// <returns></returns>
        public static TValue GetFromStatic<TParam, TValue>(string cachedKey, TParam param, Func<TParam, MemoryCacheOptions<TValue>> loadRequired)
        {
            var current = Get<TValue>(cachedKey);
            if (current == null)
            {
                EnsureLock(cachedKey);

                lock (LOCKS[cachedKey])
                {
                    current = MemoryCache.Get<TValue>(cachedKey);
                    if (current == null)
                    {
                        var cacheOptions = loadRequired(param);
                        current = cacheOptions.DataToCache;

                        cacheOptions.CacheIt(cachedKey);

                        return (TValue)current;
                    }
                    else
                    {
                        return (TValue)current;
                    }
                }
            }
            else
            {
                return (TValue)current;
            }
        }

        static void EnsureLock(string cachedKey)
        {
            if (!LOCKS.ContainsKey(cachedKey))
            {
                lock (LOCKS)
                {
                    if (!LOCKS.ContainsKey(cachedKey))
                    {
                        LOCKS[cachedKey] = new object();
                    }
                }
            }
        }
        #endregion
    }

    /// <summary>
    /// 缓存数据选项
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MemoryCacheOptions<T>
    {

        /// <summary>
        /// 使用绝对过期时间：UTC
        /// </summary>
        public DateTime? AbsoluteTime { get; set; }

        /// <summary>
        /// 使用相对过期时间（最后一次访问）
        /// </summary>
        public TimeSpan? TimeSpan { get; set; }

        /// <summary>
        /// 要缓存的数据
        /// </summary>
        public T DataToCache { get; set; }

        /// <summary>
        /// 缓存本选项后需要缓存的项目，Key是缓存Key
        /// </summary>
        public Dictionary<string, MemoryCacheOptions<T>> AfterCaches { get; set; }

        public void CacheIt(string cachedKey)
        {
            if (string.IsNullOrEmpty(cachedKey) || DataToCache == null) return;

            if (AbsoluteTime.HasValue)
                MemoryCacheHelper<T>.MemoryCache.Set<T>(cachedKey, DataToCache, AbsoluteTime.Value);
            else if (TimeSpan.HasValue)
                MemoryCacheHelper<T>.MemoryCache.Set<T>(cachedKey, DataToCache, TimeSpan.Value);
            else
                MemoryCacheHelper<T>.MemoryCache.Set<T>(cachedKey, DataToCache);

            if (AfterCaches != null)
            {
                foreach (var item in AfterCaches) item.Value.CacheIt(item.Key);
            }
        }

    }
}
